package com.tiantian.system.job;

import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.aizuda.snailjob.client.job.core.annotation.JobExecutor;
import com.aizuda.snailjob.client.model.ExecuteResult;
import com.tiantian.api.RemoteJudgeService;
import com.tiantian.api.RemoteUserService;
import com.tiantian.api.RemoteUserSolveService;
import com.tiantian.api.domain.vo.RemoteJudgerVo;
import com.tiantian.api.domain.vo.RemoteUserVo;
import com.tiantian.common.core.constant.RabbitMQConstants;
import com.tiantian.common.core.constant.SystemConfigConstants;
import com.tiantian.common.core.domain.dto.AnswerTimeDAO;
import com.tiantian.common.core.exception.BusinessException;
import com.tiantian.common.core.utils.DateUtils;
import com.tiantian.common.core.utils.StreamUtils;
import com.tiantian.common.log.enums.BusinessType;
import com.tiantian.common.redis.utils.RedisUtils;
import com.tiantian.mail.utils.MailUtils;
import com.tiantian.system.domain.entity.SysOperLog;
import com.tiantian.system.service.SysOperLogService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

@Slf4j
@Component
@RequiredArgsConstructor
public class JxJobExecutor {

    @DubboReference
    private final RemoteUserService remoteUserService;
    @DubboReference
    private final RemoteUserSolveService remoteUserSolveService;
    @DubboReference
    private final RemoteJudgeService remoteJudgeService;
    private final SysOperLogService sysOperLogService;
    private final RabbitTemplate rabbitTemplate;

    @JobExecutor(name = "allotNewcomerJobExecute")
    public ExecuteResult allotNewcomerJobExecute() {
        AnswerTimeDAO ansTime = RedisUtils.getCacheObject(SystemConfigConstants.SYSTEM_CONFIG_ANSWER_TIME_KEY);
        if (ObjectUtil.isEmpty(ansTime)) {
            throw new BusinessException("答题时间未设置请联系管理员");
        }
        // 获取当前时间
        LocalDateTime localDateTimeNow = DateUtils.getCurrentDateTime();
        LocalDateTime endTime = LocalDateTimeUtil.parse(ansTime.getDate() + " " + ansTime.getEnd(), "yyyy-MM-dd HH:mm:ss");
        // 判断是否已经过了答题时间
        if (localDateTimeNow.isAfter(endTime)) {
            // 执行分配任务
            List<RemoteUserVo> filteredNewcomerList = filterRepliedUsers(remoteUserService.getNewcomerList(null));
            // 获取出所有判题人
            List<RemoteJudgerVo> judgerList = remoteJudgeService.getJudgerList(null);
            // 进行分配
            assignNewcomerToJudger(filteredNewcomerList, judgerList);
            // 批量更新至数据库
            int count = remoteJudgeService.allotAnswererForJudger(judgerList);
            for (RemoteUserVo remoteUserVo : filteredNewcomerList) {
                Long userId = remoteUserVo.getUserId();
                // 发布消息-进行AI评分
                try {
                    rabbitTemplate.convertAndSend(RabbitMQConstants.AI_JUDGE_EXCHANGE_NAME, "", userId);
                } catch (Exception e) {
                    String errorMsg = StrUtil.format("AI判题失败,失败用户ID:{}, 姓名: {}, 请及时处理", userId, remoteUserVo.getNickName());
                    sendAdminErrorInfo("AI判题失败", errorMsg);
                    log.error(errorMsg);
                }
            }
            // 将任务关闭
            boolean exeSuccess = JobHelper.openTask(3);
            // 记录日志
            StringBuilder logInfo = new StringBuilder();
            for (RemoteJudgerVo judgerVo : judgerList) {
                String judgerId = judgerVo.getJudgerId();
                String judgerName = judgerVo.getJudgerName();
                List<Long> answererIds = judgerVo.getAnswererIds();
                logInfo.append("judgerId:[").append(judgerId).append("] ")
                        .append("judgerName:[").append(judgerName).append("] ")
                        .append("answererIds:[");
                for (int i = 0; i < answererIds.size(); i++) {
                    logInfo.append(answererIds.get(i));
                    if (i < answererIds.size() - 1) {
                        logInfo.append(",");
                    }
                }
                logInfo.append("]\n");
            }
            saveLogInfo(judgerList, count, logInfo);
            if (exeSuccess && judgerList.size() == count) {
                // 发送邮件给所有判题人进行提醒
                sendJudgerEmail(judgerList, logInfo.toString());
                return ExecuteResult.success("分配完毕,日志信息: " + logInfo);
            } else {
                // 发送邮件进行告警
                sendAdminErrorInfo("题目分配时出现异常,请到snail-job中查看错误日志", logInfo.toString());
                return ExecuteResult.failure("分配任务时出现异常,日志信息: " + logInfo);
            }
        }
        return ExecuteResult.success(StrUtil.format("当前时间:[{}],未过答题时间:[{}],等待分配...", localDateTimeNow, endTime));
    }

    private void sendJudgerEmail(List<RemoteJudgerVo> judgerList, String logInfo) {
        List<String> tos = StreamUtils.toList(judgerList, RemoteJudgerVo::getEmail);
        MailUtils.send(tos, "题目分配成功,请尽快进行判题", logInfo, false);
    }

    private void sendAdminErrorInfo(String title, String logInfo) {
        RemoteUserVo remoteUserVo = remoteUserService.getUserById(1L);
        MailUtils.send(remoteUserVo.getEmail(), title, logInfo, false);
    }

    private void saveLogInfo(List<RemoteJudgerVo> judgerList, int count, StringBuilder logInfo) {
        SysOperLog operLog = new SysOperLog();
        operLog.setTitle("定时任务:定时任务分配判题人");
        operLog.setMethod("com.tiantian.system.job.allotNewcomerJobExecute()");
        operLog.setOperName("SYSTEM");
        operLog.setOperIp("10.226.8.14:7700/snail-job");
        operLog.setStatus(judgerList.size() == count ? 0 : 1);
        operLog.setBusinessType(BusinessType.ALLOT_NEWCOMER.ordinal());
        operLog.setOperParam(logInfo.toString());
        operLog.setRequestMethod("SNAIL-JOB");
        operLog.setOperTime(LocalDateTime.now());
        sysOperLogService.insertOperLog(operLog);
    }

    public void assignNewcomerToJudger(List<RemoteUserVo> newcomerList, List<RemoteJudgerVo> judgerList) {
        // 重置 防止分配重复
        for (RemoteJudgerVo judgerVo : judgerList) {
            judgerVo.setAnswererIds(new ArrayList<>());
        }
        // 打乱顺序
        Collections.shuffle(newcomerList);
        Collections.shuffle(judgerList);
        if (newcomerList.size() > judgerList.size()) {
            for (int i = 0; i < newcomerList.size(); i++) {
                judgerList.get(i % judgerList.size())           // 获取判题人
                        .getAnswererIds()                       // 获取被判新人集合
                        .add(newcomerList.get(i).getUserId());  // 添加新人Id
            }
        } else {
            int newcomerCount = newcomerList.size();
            int judgerCount = judgerList.size();
            // 每个判题人平均分配的新人数
            int newcomerPerJudger = newcomerCount / judgerCount;
            // 多余新人的数量
            int remainingNewcomers = newcomerCount % judgerCount;
            for (int i = 0; i < newcomerPerJudger * judgerCount; i++) {
                judgerList.get(i % judgerList.size())
                        .getAnswererIds()
                        .add(newcomerList.get(i).getUserId());
            }
            for (int i = 0; i < remainingNewcomers; i++) {
                // 将多余的新人分配给前面的判题人
                judgerList.get(i % judgerList.size())
                        .getAnswererIds()
                        .add(newcomerList.get(newcomerPerJudger * judgerCount + i).getUserId());
            }

        }
    }

    private List<RemoteUserVo> filterRepliedUsers(List<RemoteUserVo> newcomerList) {
        List<RemoteUserVo> filteredNewcomers = new ArrayList<>();
        for (RemoteUserVo sysUser : newcomerList) {
            Long userId = sysUser.getUserId();
            // 判断是否有提交记录
            if (remoteUserSolveService.getLastReplyTime(userId) != null) {
                filteredNewcomers.add(sysUser);
            } else {
                // 设置为零分
                remoteUserService.setNewcomerTotalScore(userId, 0);
            }
        }
        return filteredNewcomers;
    }

}